package com.boot.admin.modules.sys.ai.service;

import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.web.socket.*;

import java.util.concurrent.ConcurrentHashMap;

/**
 * 实时语音WebSocket服务
 *
 * @author 李飞洋
 * @date 2025/9/24
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class RealtimeWebSocketService implements WebSocketHandler {

    private final RealtimeVoiceService realtimeVoiceService;

    // 存储WebSocket会话信息
    private final ConcurrentHashMap<String, SessionInfo> activeSessions = new ConcurrentHashMap<>();

    @Override
    public void afterConnectionEstablished(WebSocketSession session) throws Exception {
        log.info("实时语音WebSocket连接建立: {}", session.getId());

        // 从查询参数中获取对话信息
        String conversationId = extractQueryParam(session, "conversationId");
        String roleId = extractQueryParam(session, "roleId");
        String userId = extractQueryParam(session, "userId");

        if (conversationId == null || roleId == null || userId == null) {
            log.warn("WebSocket连接缺少必要参数: conversationId={}, roleId={}, userId={}",
                    conversationId, roleId, userId);
            sendErrorMessage(session, "连接参数不完整，需要提供conversationId、roleId和userId");
            session.close();
            return;
        }

        // 存储会话信息
        SessionInfo sessionInfo = new SessionInfo();
        sessionInfo.setSession(session);
        sessionInfo.setConversationId(conversationId);
        sessionInfo.setRoleId(roleId);
        sessionInfo.setUserId(userId);
        sessionInfo.setConnectedTime(System.currentTimeMillis());

        activeSessions.put(session.getId(), sessionInfo);

        // 初始化对话状态
        realtimeVoiceService.initConversationState(conversationId, roleId, userId);

        log.info("实时语音会话建立成功 - SessionId: {}, ConversationId: {}, RoleId: {}, UserId: {}",
                session.getId(), conversationId, roleId, userId);

        // 发送连接成功消息
        sendConnectionSuccessMessage(session, conversationId, roleId);
    }

    @Override
    public void handleMessage(WebSocketSession session, WebSocketMessage<?> message) throws Exception {
        SessionInfo sessionInfo = activeSessions.get(session.getId());
        if (sessionInfo == null) {
            log.warn("未找到会话信息: {}", session.getId());
            return;
        }

        if (message instanceof BinaryMessage) {
            // 处理二进制音频流数据
            handleAudioStream(session, (BinaryMessage) message, sessionInfo);
        } else if (message instanceof TextMessage) {
            // 处理文本控制消息
            handleControlMessage(session, (TextMessage) message, sessionInfo);
        }
    }

    @Override
    public void handleTransportError(WebSocketSession session, Throwable exception) throws Exception {
        log.error("实时语音WebSocket传输错误: {}", session.getId(), exception);
        cleanupSession(session);
    }

    @Override
    public void afterConnectionClosed(WebSocketSession session, CloseStatus closeStatus) throws Exception {
        log.info("实时语音WebSocket连接关闭: {}, 状态: {}", session.getId(), closeStatus);
        cleanupSession(session);
    }

    @Override
    public boolean supportsPartialMessages() {
        return true;
    }

    /**
     * 处理音频流数据
     */
    private void handleAudioStream(WebSocketSession session, BinaryMessage message, SessionInfo sessionInfo) {
        try {
            log.debug("接收到音频流数据: {} bytes, 会话: {}",
                    message.getPayload().remaining(), session.getId());

            // 委托给实时语音服务处理
            realtimeVoiceService.handleVoiceStream(session, message, sessionInfo.getConversationId());

        } catch (Exception e) {
            log.error("处理音频流失败", e);
            sendErrorMessage(session, "处理音频流失败: " + e.getMessage());
        }
    }

    /**
     * 处理控制消息
     */
    private void handleControlMessage(WebSocketSession session, TextMessage message, SessionInfo sessionInfo) {
        try {
            String payload = message.getPayload();
            log.debug("接收到控制消息: {}", payload);

            JSONObject controlMsg = JSONUtil.parseObj(payload);
            String action = controlMsg.getStr("action");

            switch (action) {
                case "start_recording":
                    handleStartRecording(session, sessionInfo);
                    break;
                case "stop_recording":
                    handleStopRecording(session, sessionInfo);
                    break;
                case "pause_recording":
                    handlePauseRecording(session, sessionInfo);
                    break;
                case "resume_recording":
                    handleResumeRecording(session, sessionInfo);
                    break;
                case "get_status":
                    handleGetStatus(session, sessionInfo);
                    break;
                case "ping":
                    handlePing(session);
                    break;
                default:
                    log.warn("未知控制命令: {}", action);
                    sendErrorMessage(session, "未知控制命令: " + action);
            }

        } catch (Exception e) {
            log.error("处理控制消息失败", e);
            sendErrorMessage(session, "处理控制消息失败: " + e.getMessage());
        }
    }

    /**
     * 开始录音
     */
    private void handleStartRecording(WebSocketSession session, SessionInfo sessionInfo) {
        try {
            sessionInfo.setRecording(true);
            sessionInfo.setRecordingStartTime(System.currentTimeMillis());

            // 通知RealtimeVoiceService建立ASR连接
            realtimeVoiceService.startRecording(sessionInfo.getConversationId(), session);

            JSONObject response = new JSONObject();
            response.put("type", "recording_started");
            response.put("conversationId", sessionInfo.getConversationId());
            response.put("timestamp", System.currentTimeMillis());

            session.sendMessage(new TextMessage(response.toString()));
            log.info("开始录音 - 会话: {}, 对话: {}", session.getId(), sessionInfo.getConversationId());

        } catch (Exception e) {
            log.error("开始录音失败", e);
            sendErrorMessage(session, "开始录音失败: " + e.getMessage());
        }
    }

    /**
     * 停止录音
     */
    private void handleStopRecording(WebSocketSession session, SessionInfo sessionInfo) {
        try {
            sessionInfo.setRecording(false);
            long recordingDuration = System.currentTimeMillis() - sessionInfo.getRecordingStartTime();

            // 关键：通知RealtimeVoiceService处理录音结束
            realtimeVoiceService.handleRecordingStop(
                    session,
                    sessionInfo.getConversationId(),
                    sessionInfo.getRoleId()
            );

            JSONObject response = new JSONObject();
            response.put("type", "recording_stopped");
            response.put("conversationId", sessionInfo.getConversationId());
            response.put("recordingDuration", recordingDuration);
            response.put("timestamp", System.currentTimeMillis());

            session.sendMessage(new TextMessage(response.toString()));
            log.info("停止录音 - 会话: {}, 对话: {}, 录音时长: {}ms",
                    session.getId(), sessionInfo.getConversationId(), recordingDuration);

        } catch (Exception e) {
            log.error("停止录音失败", e);
            sendErrorMessage(session, "停止录音失败: " + e.getMessage());
        }
    }

    /**
     * 暂停录音
     */
    private void handlePauseRecording(WebSocketSession session, SessionInfo sessionInfo) {
        try {
            sessionInfo.setPaused(true);

            JSONObject response = new JSONObject();
            response.put("type", "recording_paused");
            response.put("conversationId", sessionInfo.getConversationId());
            response.put("timestamp", System.currentTimeMillis());

            session.sendMessage(new TextMessage(response.toString()));
            log.info("暂停录音 - 会话: {}", session.getId());

        } catch (Exception e) {
            log.error("暂停录音失败", e);
            sendErrorMessage(session, "暂停录音失败: " + e.getMessage());
        }
    }

    /**
     * 恢复录音
     */
    private void handleResumeRecording(WebSocketSession session, SessionInfo sessionInfo) {
        try {
            sessionInfo.setPaused(false);

            JSONObject response = new JSONObject();
            response.put("type", "recording_resumed");
            response.put("conversationId", sessionInfo.getConversationId());
            response.put("timestamp", System.currentTimeMillis());

            session.sendMessage(new TextMessage(response.toString()));
            log.info("恢复录音 - 会话: {}", session.getId());

        } catch (Exception e) {
            log.error("恢复录音失败", e);
            sendErrorMessage(session, "恢复录音失败: " + e.getMessage());
        }
    }

    /**
     * 获取状态
     */
    private void handleGetStatus(WebSocketSession session, SessionInfo sessionInfo) {
        try {
            JSONObject status = new JSONObject();
            status.put("type", "status");
            status.put("conversationId", sessionInfo.getConversationId());
            status.put("recording", sessionInfo.isRecording());
            status.put("paused", sessionInfo.isPaused());
            status.put("connectedTime", sessionInfo.getConnectedTime());
            status.put("recordingStartTime", sessionInfo.getRecordingStartTime());
            status.put("timestamp", System.currentTimeMillis());

            session.sendMessage(new TextMessage(status.toString()));

        } catch (Exception e) {
            log.error("获取状态失败", e);
            sendErrorMessage(session, "获取状态失败: " + e.getMessage());
        }
    }

    /**
     * 处理心跳
     */
    private void handlePing(WebSocketSession session) {
        try {
            JSONObject pong = new JSONObject();
            pong.put("type", "pong");
            pong.put("timestamp", System.currentTimeMillis());

            session.sendMessage(new TextMessage(pong.toString()));

        } catch (Exception e) {
            log.error("处理心跳失败", e);
        }
    }

    /**
     * 清理会话资源
     */
    private void cleanupSession(WebSocketSession session) {
        SessionInfo sessionInfo = activeSessions.remove(session.getId());
        if (sessionInfo != null) {
            // 清理实时语音服务中的会话资源
            realtimeVoiceService.cleanupSession(session.getId());

            log.info("清理实时语音会话资源 - SessionId: {}, ConversationId: {}",
                    session.getId(), sessionInfo.getConversationId());
        }
    }

    /**
     * 发送连接成功消息
     */
    private void sendConnectionSuccessMessage(WebSocketSession session, String conversationId, String roleId) {
        try {
            JSONObject message = new JSONObject();
            message.put("type", "connection_established");
            message.put("conversationId", conversationId);
            message.put("roleId", roleId);
            message.put("sessionId", session.getId());
            message.put("timestamp", System.currentTimeMillis());

            session.sendMessage(new TextMessage(message.toString()));

        } catch (Exception e) {
            log.error("发送连接成功消息失败", e);
        }
    }

    /**
     * 发送错误消息
     */
    private void sendErrorMessage(WebSocketSession session, String error) {
        try {
            JSONObject message = new JSONObject();
            message.put("type", "error");
            message.put("error", error);
            message.put("timestamp", System.currentTimeMillis());

            session.sendMessage(new TextMessage(message.toString()));

        } catch (Exception e) {
            log.error("发送错误消息失败", e);
        }
    }

    /**
     * 从WebSocket会话中提取查询参数
     */
    private String extractQueryParam(WebSocketSession session, String paramName) {
        try {
            String query = session.getUri().getQuery();
            if (query != null) {
                String[] params = query.split("&");
                for (String param : params) {
                    if (param.startsWith(paramName + "=")) {
                        return param.substring(paramName.length() + 1);
                    }
                }
            }
        } catch (Exception e) {
            log.error("提取查询参数失败: {}", paramName, e);
        }
        return null;
    }

    /**
     * 获取活跃会话数
     */
    public int getActiveSessionCount() {
        return activeSessions.size();
    }

    /**
     * 获取指定对话的活跃会话数
     */
    public int getActiveSessionCount(String conversationId) {
        return (int) activeSessions.values().stream()
                .filter(session -> conversationId.equals(session.getConversationId()))
                .count();
    }

    /**
     * 会话信息内部类
     */
    private static class SessionInfo {
        private WebSocketSession session;
        private String conversationId;
        private String roleId;
        private String userId;
        private long connectedTime;
        private boolean recording = false;
        private boolean paused = false;
        private long recordingStartTime;

        public WebSocketSession getSession() {
            return session;
        }

        public void setSession(WebSocketSession session) {
            this.session = session;
        }

        public String getConversationId() {
            return conversationId;
        }

        public void setConversationId(String conversationId) {
            this.conversationId = conversationId;
        }

        public String getRoleId() {
            return roleId;
        }

        public void setRoleId(String roleId) {
            this.roleId = roleId;
        }

        public String getUserId() {
            return userId;
        }

        public void setUserId(String userId) {
            this.userId = userId;
        }

        public long getConnectedTime() {
            return connectedTime;
        }

        public void setConnectedTime(long connectedTime) {
            this.connectedTime = connectedTime;
        }

        public boolean isRecording() {
            return recording;
        }

        public void setRecording(boolean recording) {
            this.recording = recording;
        }

        public boolean isPaused() {
            return paused;
        }

        public void setPaused(boolean paused) {
            this.paused = paused;
        }

        public long getRecordingStartTime() {
            return recordingStartTime;
        }

        public void setRecordingStartTime(long recordingStartTime) {
            this.recordingStartTime = recordingStartTime;
        }
    }
}